home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / pluginy Firefox / 398 / 398.xpi / chrome / forecastfox.jar / content / utilities / disk-service.js next >
Text File  |  2010-02-04  |  22KB  |  773 lines

  1. /*------------------------------------------------------------------------------
  2.   Copyright (c) 2008 Ensolis, LLC. All Rights Reserved.
  3.   ----------------------------------------------------------------------------*/
  4.  
  5. /******************************************************************************
  6.  * File I/O Constants
  7.  *****************************************************************************/
  8. const MODE_RDONLY = 0x01;
  9. const MODE_WRONLY = 0x02;
  10. const MODE_CREATE = 0x08;
  11. const MODE_APPEND = 0x10;
  12. const MODE_TRUNCATE = 0x20; 
  13.  
  14. const XML_MIMETYPE = "text/xml";
  15. const XML_ENCODING = "UTF-8";
  16. const TEXT_MIMETYPE = "text/plain";
  17. const TEXT_ENCODING = "UTF-8";
  18. /******************************************************************************
  19.  * Create a error message to log.
  20.  * 
  21.  * @param   Original message.
  22.  * @param   Prefix to the message.
  23.  * @return  Message used for logging.
  24.  *****************************************************************************/
  25. function createMessage(aMessage, aPrefix)
  26. {
  27.   return (new Date()).toUTCString() + ": " + aPrefix + ": " + aMessage + "\r\n";
  28. }
  29.  
  30. /******************************************************************************
  31.  * Reads a file from disk and creates a dom document or text string.  
  32.  * This implements the nsIRunnable interface so it could 
  33.  * be dispatched to a thread.
  34.  * 
  35.  * @param   File to read.
  36.  * @param   The mimetype to read.
  37.  * @return  The content property can be read once the reader has run.
  38.  *****************************************************************************/
  39. function FileReader(aFile, aMimetype)
  40. {
  41.   this.file = aFile;
  42.   this.mimetype = aMimetype;
  43. }
  44. FileReader.prototype = {
  45.   _content: null,
  46.   _file: null,
  47.   _mimetype: null,
  48.   
  49.   get content() { return this._content; },
  50.   set content(aVal) { this._content = aVal; },
  51.     
  52.   get file() { return this._file; },
  53.   set file(aVal) { this._file = aVal; },
  54.     
  55.   get mimetype() { return this._mimetype; },
  56.   set mimetype(aVal) { this._mimetype = aVal; },
  57.              
  58.   ///////////////////////////
  59.   // nsISupports
  60.   QueryInterface: function FileReader_QueryInterface(aIID)
  61.   {
  62.     if (!aIID.equals(Ci.nsIRunnable) &&      
  63.         !aIID.equals(Ci.nsISupports))
  64.       throw Cr.NS_ERROR_NO_INTERFACE;
  65.     return this;
  66.   },
  67.              
  68.   ///////////////////////////
  69.   // nsIRunnable      
  70.   run: function FileReader_run()
  71.   {
  72.     //make sure the file exists
  73.     if (!this.file.exists()) {
  74.       this.content = null;
  75.       this.file = null;
  76.       this.mimetype = null;                
  77.       return;
  78.     }
  79.       
  80.     //create file input stream
  81.     var fiStream = Cc["@mozilla.org/network/file-input-stream;1"].
  82.                    createInstance(Ci.nsIFileInputStream);
  83.     
  84.     //initialize stream  
  85.     fiStream.init(this.file, MODE_RDONLY, PERMS_FILE, false);
  86.  
  87.     //read XML mimetype
  88.     if (this.mimetype == XML_MIMETYPE)
  89.       this._readXML(fiStream);
  90.       
  91.     //read text mimetype
  92.     else
  93.       this._readText(fiStream);
  94.     
  95.     //close stream
  96.     fiStream.close();
  97.       
  98.     //remove variable references
  99.     this.file = null; 
  100.     this.mimetype = null;            
  101.   },
  102.   
  103.   /**
  104.    * Read a XML file.
  105.    *
  106.    * @param   The file input stream.
  107.    */  
  108.   _readXML: function FileReader__readXML(aStream)
  109.   {  
  110.     /**
  111.      * to support earlier versions we need to use a buffered
  112.      * input stream.  See bug #287409
  113.      */
  114.     var biStream = Cc["@mozilla.org/network/buffered-input-stream;1"].
  115.                    createInstance(Ci.nsIBufferedInputStream);
  116.     biStream.init(aStream, 64 * 1024);  
  117.             
  118.     //parse dom document from stream
  119.     var domParser = Cc["@mozilla.org/xmlextras/domparser;1"].
  120.                     createInstance(Ci.nsIDOMParser);
  121.     this.content = domParser.parseFromStream(biStream, XML_ENCODING, 
  122.                                              biStream.available(), 
  123.                                              XML_MIMETYPE);
  124.       
  125.     //close the buffer input stream
  126.     biStream.close();  
  127.   },
  128.   
  129.   /**
  130.    * Read a text file.
  131.    *
  132.    * @param   The file input stream.
  133.    */
  134.   _readText: function FileReader__readText(aStream)
  135.   {
  136.     //get a scriptable stream
  137.     var siStream = Cc["@mozilla.org/scriptableinputstream;1"].
  138.                    createInstance(Ci.nsIScriptableInputStream);
  139.     siStream.init(aStream);   
  140.            
  141.     //convert to text encoding
  142.     var converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"].
  143.                     createInstance(Ci.nsIScriptableUnicodeConverter);
  144.     converter.charset = TEXT_ENCODING;
  145.     
  146.     //read stream into a string
  147.     var content = new String();
  148.     while (siStream.available() > 0) {
  149.       var chunk = siStream.read(siStream.available());
  150.       content += converter.ConvertToUnicode(chunk);      
  151.     }
  152.     this.content = content;
  153.       
  154.     //close scritable stream
  155.     siStream.close();     
  156.   }
  157. };
  158.  
  159. /******************************************************************************
  160.  * Writes content to disk.  The content can be a string, or a dom document.  
  161.  * This implements the nsIRunnable interface so it could be dispatched.
  162.  * to a thread.
  163.  * 
  164.  * @param   File to write.
  165.  * @param   Content to write.  Either a dom document, or a string.
  166.  * @param   If a backup copy of the file should be created. 
  167.  * @param   The mimetype of the content to write.  Used to determine write 
  168.  *          mode and if content is dom or string.
  169.  *****************************************************************************/
  170. function FileWriter(aFile, aContent, aBackup, aMimeType)
  171. {
  172.   //set variables
  173.   this.content = aContent;
  174.   this.file = aFile;
  175.   this.backup = aBackup;
  176.   this.mimetype = aMimeType;
  177. }
  178. FileWriter.prototype = {
  179.   _content: null,
  180.   _file: null,
  181.   _backup: null,
  182.   _mimetype: null,
  183.   
  184.   get content() { return this._content; },
  185.   set content(aVal) { this._content = aVal; },
  186.     
  187.   get file() { return this._file; },
  188.   set file(aVal) { this._file = aVal; },
  189.   
  190.   get backup() { return this._backup; },
  191.   set backup(aVal) { this._backup = aVal; },
  192.   
  193.   get mimetype() { return this._mimetype; },
  194.   set mimetype(aVal) { this._mimetype = aVal; },
  195.                
  196.   ///////////////////////////
  197.   // nsISupports
  198.   QueryInterface: function FileWriter_QueryInterface(aIID)
  199.   {
  200.     if (!aIID.equals(Ci.nsIRunnable) &&      
  201.         !aIID.equals(Ci.nsISupports))
  202.       throw Cr.NS_ERROR_NO_INTERFACE;
  203.     return this;
  204.   },
  205.              
  206.   ///////////////////////////
  207.   // nsIRunnable  
  208.   run: function FileWriter_run()
  209.   {
  210.     //make sure the file exists
  211.     if (!this.file.exists())
  212.       this.file.create(this.file.NORMAL_FILE_TYPE, PERMS_FILE);
  213.       
  214.     //create a file output stream
  215.     var foStream = Cc["@mozilla.org/network/safe-file-output-stream;1"].
  216.                    createInstance(Ci.nsIFileOutputStream);
  217.        
  218.     //write XML mimetype
  219.     if (this.mimetype == XML_MIMETYPE)
  220.       this._writeXML(foStream);
  221.     
  222.     //write text mimetype
  223.     else
  224.       this._writeText(foStream);
  225.     
  226.     //close stream
  227.     if (foStream instanceof Ci.nsISafeOutputStream)
  228.       foStream.finish();
  229.     foStream.close();
  230.     
  231.     //create a backup
  232.     if (this.backup)
  233.       this._createBackup();
  234.     
  235.     //remove variable references
  236.     this.content = null;
  237.     this.file = null;
  238.     this.backup = null;  
  239.     this.mimetype = null;              
  240.   },
  241.   
  242.   /**
  243.    * Write a xml file.
  244.    *
  245.    * @param   The file output stream.
  246.    */    
  247.   _writeXML: function FileWriter__writeXML(aStream)
  248.   {  
  249.     //initialize stream  
  250.     aStream.init(this.file, (MODE_WRONLY | MODE_TRUNCATE), PERMS_FILE, 0);
  251.                  
  252.     //serialize to stream
  253.     var domSerializer = Cc["@mozilla.org/xmlextras/xmlserializer;1"].
  254.                         createInstance(Ci.nsIDOMSerializer);
  255.     domSerializer.serializeToStream(this.content, aStream, XML_ENCODING);        
  256.   },
  257.   
  258.   /**
  259.    * Write a text file.
  260.    *
  261.    * @param   The file output stream.
  262.    */  
  263.   _writeText: function FileWriter__writeText(aStream)
  264.   {
  265.     //initialize stream               
  266.     aStream.init(this.file, (MODE_WRONLY | MODE_APPEND), PERMS_FILE, 0);
  267.  
  268.     //get the string converter
  269.     var converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"].
  270.                     createInstance(Ci.nsIScriptableUnicodeConverter);
  271.     converter.charset = TEXT_ENCODING;
  272.     
  273.     //write to stream
  274.     var chunk = converter.ConvertFromUnicode(this.content);
  275.     aStream.write(chunk, chunk.length);
  276.     var fin = converter.Finish();
  277.     if (fin.length > 0)
  278.       aStream.write(fin, fin.length); 
  279.   },
  280.   
  281.   /**
  282.    * Create a backup copy of the file being written to.
  283.    */    
  284.   _createBackup: function FileWriter__createBackup()
  285.   {
  286.     //replace file extension with .bak
  287.     var name = this.file.leafName;
  288.     var ext = name.substring(name.lastIndexOf(".") + 1, name.length);
  289.     name = name.replace(ext, "bak");
  290.     
  291.     //get the backup file
  292.     var backup = this.file.parent.clone();  
  293.     backup.append(name);
  294.          
  295.     //remove it if it already exists
  296.     try {
  297.       if (backup.exists())
  298.         removeFile(backup);
  299.     
  300.       //copy the new file to the backup location
  301.       this.file.copyTo(backup.parent, backup.leafName);
  302.     } catch(e) {}   
  303.   }  
  304. };
  305.  
  306. /******************************************************************************
  307.  * Interfaces used by a service for disk I/O functions.  Supplies a set
  308.  * of often used disk utilities.
  309.  * 
  310.  * @status    FROZEN
  311.  * @version   1.0
  312.  ******************************************************************************/
  313. function DiskService() 
  314. {
  315.   //setup additional interfaces
  316.   this._ifaces.push(Ci.nsIObserver);
  317.   this._ifaces.push(Ci.nsISupportsWeakReference);
  318.   
  319.   //setup a new error
  320.   this._error = Cc["@ensolis.com/forecastfox/error-item;1"].
  321.                 createInstance(Ci.ffIErrorItem);    
  322. }
  323. DiskService.prototype = {
  324.   __proto__: new ServiceBase("DiskService"),
  325.   _ioSvc: null,
  326.   _domParser: null,
  327.   _fileWriters: null,
  328.   _writeTimer: null,
  329.   _types: null,
  330.   
  331.   ///////////////////////////
  332.   // nsIObserver
  333.   observe: function DiskService_observe(aSubject, aTopic, aData)
  334.   {
  335.     if (aTopic != "timer-callback" || aSubject != this._writeTimer)
  336.       return;
  337.             
  338.     //run the next stored writers
  339.     for (var path in this._fileWriters) {
  340.       this._fileWriters[path].run();
  341.       delete this._fileWriters[path];
  342.       break;
  343.     }
  344.     
  345.     //control the starting-stopping of the timer
  346.     this._controlTimer();
  347.   },
  348.               
  349.   ////////////////////////////////
  350.   // ffIService
  351.           
  352.   /**
  353.    * Initialize the component.  Called by the manager service.
  354.    */    
  355.   start: function DiskService_start()
  356.   {
  357.     //setup writers object
  358.     this._fileWriters = {};
  359.     
  360.     //setup types object
  361.     this._types = {};
  362.     
  363.     //get io service
  364.     this._ioSvc = Cc["@mozilla.org/network/io-service;1"].
  365.                   getService(Ci.nsIIOService);
  366.     
  367.     //get the dom parser              
  368.     this._domParser = Cc["@mozilla.org/xmlextras/domparser;1"].
  369.                       createInstance(Ci.nsIDOMParser);           
  370.                      
  371.     //return success
  372.     return true;      
  373.   },
  374.   
  375.   /**
  376.    * Destroy the component.  Called by the manager service.  This may be
  377.    * called prior to start so it needs to be safe.
  378.    */  
  379.   stop: function DiskService_stop()
  380.   {
  381.     //stop the writer timer
  382.     if (this._writeTimer)
  383.       this._writeTimer.cancel();
  384.     
  385.     //clear any pending writes
  386.     if (this._fileWriters) {
  387.       for (var path in this._fileWriters) {
  388.         this._fileWriters[path].run();
  389.         delete this._fileWriters[path];
  390.       }
  391.     }
  392.     
  393.     //clear variables
  394.     this._ioSvc = null;
  395.     this._domParser = null;
  396.     this._fileWriters = null;
  397.     this._writeTimer = null;
  398.     this._types = null;
  399.   },
  400.               
  401.   ////////////////////////////////
  402.   // ffIDiskService
  403.     
  404.   /**
  405.    * Get a file from one of our directory types.  Creates a unique
  406.    * file name if the type is the temp directory.
  407.    * 
  408.    * @param   Name of the file to get.
  409.    * @param   Directory type to get the file from.
  410.    * @return  File requested.
  411.    */
  412.   get: function DiskService_get(aName, aType)
  413.   {
  414.     var file = null;  
  415.     
  416.     //use the cached type file
  417.     if (this._types.hasOwnProperty(aType))
  418.       file = this._types[aType];
  419.        
  420.     //get directory based on type  
  421.     else {
  422.       switch (aType) {
  423.       case TYPE_PROFILE:
  424.         file = getKeyedDirectory("ProfD", ["forecastfox"], true);    
  425.         break;      
  426.       case TYPE_CACHE:
  427.         file = getKeyedDirectory("ProfD", ["forecastfox", "cache"], true);
  428.         break;
  429.       case TYPE_ICONS:
  430.         file = getKeyedDirectory("ProfD", ["forecastfox", "icons"], true);
  431.         break;
  432.       case TYPE_TEMP:
  433.         file = getKeyedDirectory("TmpD", [], false);
  434.         break;
  435.       case TYPE_DEFAULTS:
  436.         file = getInstallDirectory(["defaults"]);
  437.         break;
  438.       case TYPE_WEATHERFOX:
  439.         file = getKeyedDirectory("ProfD", ["weatherfox"], false);
  440.         break;
  441.       case TYPE_ERRORS:
  442.         file = getKeyedDirectory("ProfD", ["forecastfox", "errors"], true);
  443.         break;
  444.       }
  445.       
  446.       //cache the type
  447.       this._types[aType] = file;
  448.     }
  449.     
  450.     //clone the directory and append the file name
  451.     file = file.clone();
  452.     file.append(aName);
  453.     
  454.     //create unique if temp directory
  455.     if (aType == TYPE_TEMP)
  456.       file.createUnique(file.NORMAL_FILE_TYPE, PERMS_FILE);    
  457.       
  458.     //return the file                 
  459.     return file;
  460.   },
  461.   
  462.   /**
  463.    * Copy a file.  It first removes the destination file
  464.    * and then copies to the destination file.
  465.    * 
  466.    * @param  File to be copied.
  467.    * @param  File that will be replaced.  It may or may not exist.
  468.    */    
  469.   copy: function DiskService_copy(aFrom, aTo)
  470.   {
  471.     //remove the to file if it exists
  472.     if (aTo.exists())
  473.       removeFile(aTo);
  474.       
  475.     //copy from file
  476.     aFrom.copyTo(aTo.parent, aTo.leafName);  
  477.   },
  478.  
  479.   /**
  480.    * Clear a directory of the type passed in.  This should be used 
  481.    * very carefully since will wipe out the directory.
  482.    * 
  483.    * @param   Type of directory to clear
  484.    * @param   Only remove files with cache in the name.
  485.    */  
  486.   clear: function DiskService_clear(aType, aCache)
  487.   {
  488.     //get the folder of the requested type
  489.     var folder = this.get("", aType);
  490.     if (!folder.exists())
  491.       return;
  492.       
  493.     //make sure its a directory
  494.     if (!folder.isDirectory())
  495.       return;
  496.       
  497.     //get list of files
  498.     var files = folder.directoryEntries;
  499.     while (files.hasMoreElements()) {
  500.       var file = files.getNext().QueryInterface(Ci.nsIFile);
  501.       
  502.       //remove the file regardless
  503.       if (!aCache)
  504.         removeFile(file);
  505.       
  506.       //only remove cache files
  507.       else {
  508.         var name = file.leafName;    
  509.         if (name.indexOf("cache") != -1)
  510.           removeFile(file);
  511.       }
  512.     }                
  513.   },
  514.         
  515.   /**
  516.    * Reads a file from disk and converts it to a dom document.
  517.    * 
  518.    * @param   File to read.
  519.    * @return  The dom document.
  520.    */
  521.   read: function DiskService_read(aFile)
  522.   {
  523.     return this._dispatchRead(aFile, XML_MIMETYPE);
  524.   },  
  525.   
  526.   /**
  527.    * Reads a file from disk and returns the string content.
  528.    *
  529.    * @param   File to read.
  530.    * @return  The string content.
  531.    */
  532.   readText: function DiskService_readText(aFile)
  533.   {
  534.     return this._dispatchRead(aFile, TEXT_MIMETYPE);
  535.   },  
  536.           
  537.   /**
  538.    * Writes a dom document to a file.
  539.    * 
  540.    * @param   File to write to.
  541.    * @param   Document to write.
  542.    * @param   Create a backup of the file.
  543.    * @param   Run in blocking mode.
  544.    */  
  545.   write: function DiskService_write(aFile, aDoc, aBackup, aBlocking)
  546.   {
  547.     this._dispatchWrite(aFile, aDoc, aBackup, aBlocking, XML_MIMETYPE);
  548.   },
  549.            
  550.   /**
  551.    * Writes a strings content to a file.
  552.    * 
  553.    * @param   File to write to.
  554.    * @param   String to write.
  555.    * @param   Create a backup of the file.
  556.    * @param   Run in blocking mode.   
  557.    * @param   Append to file instead of replace.
  558.    */  
  559.   writeText: function DiskService_writeText(aFile, aContent, aBackup, 
  560.                                             aBlocking, aAppend)
  561.   {
  562.     //get content of the file if we are appending  
  563.     var content = "";
  564.     if (aAppend)
  565.       content = this.readText(aFile);    
  566.     content += aContent;
  567.     
  568.     //dispatch writer
  569.     this._dispatchWrite(aFile, content, aBackup, aBlocking, TEXT_MIMETYPE);
  570.   },
  571.   
  572.   /**
  573.    * Creates an Empty dom document.  Need to pass the local name of the
  574.    * root node, doctype url, and namespace url.
  575.    * 
  576.    * @param   Local name of the root node.
  577.    * @param   Doctype url.
  578.    * @param   Namespace url.
  579.    * @return  An empty DOM document.
  580.    */  
  581.   create: function DiskService_create(aRoot, aDTD, aNS)
  582.   {
  583.     //string representation of xml
  584.     var contents = "";
  585.     contents += "<?xml version=\"1.0\"?>\n" +
  586.                 "<!DOCTYPE " + aRoot + " SYSTEM \"" + aDTD + "\">\n" +
  587.                 "<" + aRoot + " version=\"0.9.10\" xmlns=\"" + 
  588.                 aNS + "\"/>";              
  589.  
  590.     //return dom document
  591.     return this._domParser.parseFromString(contents, XML_MIMETYPE);  
  592.   },
  593.     
  594.   /*
  595.    * Test for a valid dom document.
  596.    * 
  597.    * @param   Dom document to validate.
  598.    * @param   Local name of the root node.
  599.    * @return  True if document passed and root node is correct.
  600.    */  
  601.   validate: function DiskService_validate(aDoc, aRoot)
  602.   {
  603.     //object passed
  604.     if(!aDoc)
  605.       return false;
  606.       
  607.     //incorrect root
  608.     if (aDoc.documentElement.localName != aRoot)
  609.       return false;
  610.         
  611.     return true;
  612.   },
  613.       
  614.   /**
  615.    * Gets the url of a file passed in.  If the file doesn't exist,
  616.    * a blank string is returned.
  617.    * 
  618.    * @param   File to get the url of.
  619.    * @return  Spec of the URI.
  620.    */  
  621.   getFileURL: function DiskService_getFileURL(aFile)
  622.   {
  623.     //use ioservice to create uri
  624.     var URI = this._ioSvc.newFileURI(aFile);
  625.     
  626.     //return the spec
  627.     return URI.spec;
  628.   },
  629.         
  630.   /**
  631.    * Write a message to the error log.  Any of the params can be null.
  632.    * They are skipped if null.
  633.    * 
  634.    * @param   Message to write.
  635.    * @param   Exception that occurred.
  636.    * @param   File to copy.
  637.    */
  638.   log: function DiskService_log(aMessage, aError, aFile)
  639.   {
  640.     //get the error log file
  641.     var file = this.get("errors.log", TYPE_ERRORS);
  642.     
  643.     //create a variable to hold the strings
  644.     var content = "";
  645.     
  646.     //append the new message
  647.     if (aMessage) 
  648.       content += createMessage(aMessage, "Message" );
  649.     
  650.     //append the error
  651.     if (aError) {
  652.       content += createMessage(aError.toString(), "Exception");
  653.  
  654.       //write call stack
  655.       var frame = aError.location;
  656.       while (frame) {
  657.         content += createMessage(frame.toString(), "Stack");
  658.         frame = frame.caller;
  659.       }
  660.     }
  661.     
  662.     //apend the file
  663.     if (aFile) {
  664.       
  665.       //get the error directory
  666.       var copyFile = this.get("", TYPE_ERRORS);
  667.     
  668.       //determine file name
  669.       var name = aFile.leafName;
  670.       if (name.indexOf(".") == -1)
  671.         name = name + "-err";
  672.       else {
  673.         var ind = name.indexOf(".");
  674.         var ext = name.substring(ind, name.length);
  675.         name = name.substring(0, ind);
  676.         name = name + "-err" + ext;
  677.       }
  678.     
  679.       //copy file to new name and directory
  680.       copyFile.append(name);
  681.       copyFile.createUnique(copyFile.NORMAL_FILE_TYPE, PERMS_FILE);
  682.       this.copy(aFile, copyFile);
  683.     
  684.       //add message
  685.       content += createMessage("Error file (" + copyFile.leafName + 
  686.                                ") created for " + aFile.leafName + ".", "File");
  687.     }
  688.     
  689.     //write the content to disk
  690.     if (content.length > 0)
  691.       this.writeText(file, content, false, false, true); 
  692.   },  
  693.               
  694.   ////////////////////////////////
  695.   // Internal Functions  
  696.     
  697.   /**
  698.    * Dispatch the file reader.  This helper function is used
  699.    * because the different functions in the interface are used for different
  700.    * mimetypes.
  701.    *
  702.    * @param   File to read from.
  703.    * @param   Mimetype being read. 
  704.    */      
  705.   _dispatchRead: function DiskService__dispatchRead(aFile, aMimeType)
  706.   {
  707.     //perform any pending writes before reading
  708.     var path = aFile.path;
  709.     if (this._fileWriters.hasOwnProperty(path))
  710.       return this._fileWriters[path].content;
  711.       
  712.     //create a file reader and dispatch it.
  713.     var reader = new FileReader(aFile, aMimeType);
  714.     reader.run();
  715.       
  716.     //get the created document and return it
  717.     return reader.content;   
  718.   },
  719.     
  720.   /**
  721.    * Dispatch the file writer.  This helper function is used
  722.    * because the different functions in the interface are used for different
  723.    * mimetypes.
  724.    *
  725.    * @param   File to write to.
  726.    * @param   string to write or document to write.
  727.    * @param   Create a backup of the file.
  728.    * @param   Run in blocking mode.
  729.    * @param   Mimetype being written. 
  730.    */    
  731.   _dispatchWrite: function DiskService__dispatchWrite(aFile, aContent, aBackup,
  732.                                                       aBlocking, aMimeType)
  733.   {
  734.     //if we have a pending write to the file remove it
  735.     if (this._fileWriters.hasOwnProperty(aFile.path))
  736.       delete this._fileWriters[aFile.path];
  737.     
  738.     //create the file writer
  739.     var writer = new FileWriter(aFile, aContent, aBackup, aMimeType);                  
  740.       
  741.     //run inline if blocking
  742.     if (aBlocking)
  743.       writer.run();
  744.       
  745.     //add to pending writer list
  746.     else
  747.       this._fileWriters[aFile.path] = writer;
  748.     
  749.     // control the starting or stopping of the write timer
  750.     this._controlTimer();
  751.   },
  752.   
  753.   _controlTimer: function DiskService__controlTimer() {
  754.   
  755.     // determine if we have pending writes
  756.     var pending = false;
  757.     for (var path in this._fileWriters) {
  758.       pending = true;
  759.       break;
  760.     }
  761.     
  762.     // we have pending writes and the timer isn't started
  763.     if (pending && !this._writeTimer) {
  764.         this._writeTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
  765.         this._writeTimer.init(this, 60*1000, Ci.nsITimer.TYPE_REPEATING_SLACK);
  766.  
  767.     // no pending writes and the timer is started
  768.     } else if (!pending && this._writeTimer) {
  769.       this._writeTimer.cancel();
  770.       this._writeTimer = null;
  771.     }
  772.   }
  773. };